//共用基本库
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

//伺服电机所用的库
#include <memory.h>
#include "hi_time.h"

//使用GPIO时，使用以下三个共用基本库
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "iot_gpio.h"

//传感器所用库adc读取可燃气体所用，aht20, iot_i2c为读取温湿度所用
//其他为传感器共用基本库
#include "hi_adc.h"
#include "aht20.h"
#include "iot_i2c.h"
#include "iot_errno.h"
#include "hi_io.h"

//hal的wifi和mqtt协议库
#include "hal_bsp_wifi.h"
#include "hal_bsp_mqtt.h"
#include "hal_bsp_ap3216c.h"
#include "hal_bsp_aw2013.h"
#include "hal_bsp_pcf8574.h"
#include "hal_bsp_sht20.h"
#include "hal_bsp_ssd1306.h"
#include "hal_bsp_structAll.h"

//第三方cJSON库，方便接收和发送JSON数据
#include "cJSON.h"

// wifi名称，替换为自己的名称
// ElySia为默认值
#define WIFI_NAME "ElySia"
// wifi密码，替换为自己的密码
#define WIFI_PASSWORD "your_password"

// 设备ID
#define DEVICE_ID "your_path_hi3861"
// MQTT客户端ID
#define MQTT_CLIENT_ID "your_path_hi3861_0_0_2024_your_time"
// MQTT用户名
#define MQTT_USER_NAME "your_path_hi3861"
// MQTT密码
#define MQTT_PASS_WORD "your_mqtt_password"
// 华为iotda的IP地址
#define SERVER_IP_ADDR "121.36.42.100" // 截至2024/4/4的华为iotda地址，根据需要更改
// 华为iotda的的IP端口号                //截至2024/4/4的华为iotda端口，根据需要更改
#define SERVER_IP_PORT 1883
// 控制命令接受主题
#define MQTT_TOPIC_SUB_COMMANDS "$oc/devices/%s/sys/commands/#"
// 控制命令返回主题
#define MQTT_TOPIC_PUB_COMMANDS_REQ "$oc/devices/%s/sys/commands/response/request_id=%s"
#define MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ "$oc/devices//sys/commands/response/request_id="

// 从设备上传数据的主题
#define MQTT_TOPIC_PUB_PROPERTIES "$oc/devices/%s/sys/properties/report"
#define MALLOC_MQTT_TOPIC_PUB_PROPERTIES "$oc/devices//sys/properties/report"

//设置引脚
//GPIO2 --> 控制LED | GPIO1 --> 控制风扇 | GPIO0 --> 控制继电器 | GPIO10 --> 控制连接窗户的伺服电机
#define LED_TASK_GPIO 2
#define FAN_TASK_GPIO 1
#define SIG_TASK_GPIO 0
#define SG90_TSK_GPIO 10

//设置一个延迟时间，并定义高低电平
#define DELAY_TICKS_50 (50)
#define GPIO_HIGH (1)
#define GPIO_LOW (0)

#define TASK_STACK_SIZE (1024 * 10) //分配的内存空间
#define MsgQueueObjectNumber 16 // 定义消息队列对象的个数

typedef struct message_sensorData
{
    uint8_t led; // LED灯当前的状态
    uint8_t fan; // 风扇当前的状态
    uint8_t sig; // 继电器当前的状态
    uint8_t sg;  // SG90舵机状态
    float tem;   // 温度
    float hum;   // 湿度
    float gas;   // 可燃气体浓度
} msg_sensorData_t;

msg_sensorData_t sensorData = {0}; // 传感器的数据

osThreadId_t mqtt_send_task_id; // mqtt 发布数据任务ID
osThreadId_t mqtt_recv_task_id; // mqtt 接收数据任务ID

//设置mqtt任务的各项延迟
#define MQTT_SEND_TASK_TIME 3 // s
#define MQTT_RECV_TASK_TIME 1 // s
#define TASK_INIT_TIME 2      // s
#define DISPLAY_BUFF_MAX 64
#define MQTT_DATA_MAX 256
#define MS_PER_S 1000 // ms

//设置可燃气体传感器所用的数据
#define GAS_SENSOR_CHAN_NAME 5
// #define GAS_SENSOR_PIN_NAME WIFI_IOT_IO_NAME_GPIO_11
#define ADC_RESOLUTION 2048
#define VOLTAGE_5V (5.0)
#define EPS (1e-7)

//两条I2C总线的定义
#define IOT_GPIO_IDX_13 13
#define IOT_GPIO_IDX_14 14

float range_20 = 20.0f;
float range_35 = 35.0f;
float range_50 = 50.0f;

int ret = 0;
uint32_t retval = 0;

uint8_t publish_topic[MQTT_DATA_MAX] = {0};
uint8_t mqtt_data[MQTT_DATA_MAX] = {0};
uint8_t displayBuffer[DISPLAY_BUFF_MAX] = {0};

void set_angle(unsigned int duty)
{
    IoTGpioSetDir(SG90_TSK_GPIO, IOT_GPIO_DIR_OUT); // 设置GPIO10为输出模式

    // GPIO10输出x微秒高电平
    IoTGpioSetOutputVal(SG90_TSK_GPIO, IOT_GPIO_VALUE1);
    hi_udelay(duty);

    // GPIO10输出20000-x微秒低电平
    IoTGpioSetOutputVal(SG90_TSK_GPIO, IOT_GPIO_VALUE0);
    hi_udelay(20000 - duty);
}

// 伺服电机左转90°
// 此函数未应用
void engine_turn_left_90(void)
{
    for (int i = 0; i < 10; i++)
    {
        set_angle(500);
    }
}

// 伺服电机居中
void regress_middle(void)
{
    for (int i = 0; i < 10; i++)
    {
        set_angle(1500);
    }
}

// 伺服电机右转90°
void engine_turn_right_90(void)
{
    for (int i = 0; i < 10; i++)
    {
        set_angle(2500);
    }
}

//数据转换
static float ConvertToVoltage(unsigned short data)
{
    return (float)data * 1.8 * 4 / 4096;
}

/**
 * @brief 组JSON数据
 */
int Packaged_json_data(void)
{
    cJSON *root = NULL, *array = NULL, *services = NULL;
    cJSON *properties = NULL;
    int ret = 0;

    // 组JSON数据
    root = cJSON_CreateObject(); // 创建一个对象
    services = cJSON_CreateArray();
    cJSON_AddItemToObject(root, "services", services);
    array = cJSON_CreateObject();
    cJSON_AddStringToObject(array, "service_id", "attribute");
    properties = cJSON_CreateObject();
    cJSON_AddItemToObject(array, "properties", properties);
    cJSON_AddStringToObject(properties, "fan", sensorData.fan ? "ON" : "OFF");
    cJSON_AddStringToObject(properties, "led", sensorData.led ? "ON" : "OFF");
    cJSON_AddStringToObject(properties, "sig", sensorData.sig ? "ON" : "OFF");
    cJSON_AddStringToObject(properties, "sg", sensorData.sg ? "ON" : "OFF");
    cJSON_AddNumberToObject(properties, "hum", sensorData.hum);
    cJSON_AddNumberToObject(properties, "tem", sensorData.tem);
    cJSON_AddNumberToObject(properties, "gas", sensorData.tem);
    cJSON_AddItemToArray(services, array); // 将对象添加到数组中

    /* 格式化打印创建的带数组的JSON对象 */
    char *str_print = cJSON_PrintUnformatted(root);
    if (str_print != NULL)
    {
        if (strcpy_s(mqtt_data, strlen(str_print) + 1, str_print) == 0)
        {
            ret = 0;
        }
        else
        {
            ret = -1;
        }
        cJSON_free(str_print);
    }
    else
    {
        ret = -1;
    }
    if (root != NULL)
    {
        cJSON_Delete(root);
    }
    else
    {
        ret = -1;
    }
    properties = str_print = root = array = services = NULL;

    return ret;
}

/**
 * @brief MQTT  发布消息任务
 */

void mqtt_send_task_intime(void)
{
    // 即使获取设备的数据
    // 组Topic
    memset_s(publish_topic, MQTT_DATA_MAX, 0, MQTT_DATA_MAX);
    if (sprintf_s(publish_topic, MQTT_DATA_MAX, MQTT_TOPIC_PUB_PROPERTIES, DEVICE_ID) > 0)
    {
        // 组JSON数据
        Packaged_json_data();
        // 发布消息
        MQTTClient_pub(publish_topic, mqtt_data, strlen((char *)mqtt_data));
    }
}

void mqtt_send_task(void)
{
    while (1)
    {

        static char line[32] = {0};

        //初始化AHT20传感器
        while (IOT_SUCCESS != AHT20_Calibrate())
        {
            printf("AHT20 sensor init failed!\r\n");
            usleep(MS_PER_S);
        }

        // 获取温湿度传感器设备的数据
        retval = AHT20_StartMeasure();
        if (retval != IOT_SUCCESS)
        {
            printf("trigger measure failed!\r\n");
        }

        // 保存温度和湿度
        retval = AHT20_GetMeasureResult(&sensorData.tem, &sensorData.hum);
        if (retval != IOT_SUCCESS)
        {
            printf("get humidity data failed!\r\n");
        }

        unsigned short data = 0;

        //初始化可燃气体传感器
        ret = hi_adc_read(GAS_SENSOR_CHAN_NAME, &data, HI_ADC_EQU_MODEL_4, HI_ADC_CUR_BAIS_DEFAULT, 0);

        if (ret == IOT_SUCCESS)
        {
            float Vx = ConvertToVoltage(data);
            //   =>  Rx = 5/Vx - 1
            // 根据电压计算出可燃气体浓度
            if (abs(Vx) > EPS)
            {
                sensorData.gas = VOLTAGE_5V / Vx - 1;
                //在27℃，30%湿度条件下，gas的数字数值的基准值为30
                //在此条件下，gas > 36即认为可燃气体超标
            }
        }
        else
        {
            printf("\r\n hi_adc_read fail, ret=%d", ret);
        }

        // 组Topic
        memset_s(publish_topic, MQTT_DATA_MAX, 0, MQTT_DATA_MAX);
        if (sprintf_s(publish_topic, MQTT_DATA_MAX, MQTT_TOPIC_PUB_PROPERTIES, DEVICE_ID) > 0)
        {
            // 组JSON数据
            Packaged_json_data();
            // 发布消息
            MQTTClient_pub(publish_topic, mqtt_data, strlen((char *)mqtt_data));
        }
        osDelay(3000); // 延迟30秒
    }
    sleep(MQTT_SEND_TASK_TIME);
}

// MQTT数据传输校验
int get_jsonData_value(const cJSON *const object, uint8_t *value)
{
    cJSON *json_value = NULL;
    int ret = -1;
    json_value = cJSON_GetObjectItem(object, "value");
    if (json_value)
    {
        if (!strcmp(json_value->valuestring, "ON"))
        {
            *value = 1;
            json_value = NULL;
            ret = 0; // 0为成功
        }
        else if (!strcmp(json_value->valuestring, "OFF"))
        {
            *value = 0;
            json_value = NULL;
            ret = 0;
        }
    }
    json_value = NULL;
    return ret; // -1为失败
}

/**
 * @brief 解析JSON数据
 */
int Parsing_json_data(const char *payload)
{
    cJSON *root = NULL, *command_name = NULL, *paras = NULL, *value = NULL;
    cJSON *red = NULL, *green = NULL, *blue = NULL;
    int ret_code = 1;
    root = cJSON_Parse((const char *)payload);
    if (root)
    {
        // 解析JSON数据
        command_name = cJSON_GetObjectItem(root, "command_name");
        paras = cJSON_GetObjectItem(root, "paras");
        if (command_name)
        {
            if (!strcmp(command_name->valuestring, "led"))
            {
                ret_code = get_jsonData_value(paras, &sensorData.led);
            }
            else if (!strcmp(command_name->valuestring, "fan"))
            {
                ret_code = get_jsonData_value(paras, &sensorData.fan);
            }
            else if (!strcmp(command_name->valuestring, "sig"))
            {
                ret_code = get_jsonData_value(paras, &sensorData.sig);
            }
            else if (!strcmp(command_name->valuestring, "sg"))
            {
                ret_code = get_jsonData_value(paras, &sensorData.sg);
            }
        }
    }
    cJSON_Delete(root);
    root = command_name = paras = value = red = green = blue = NULL;
    //对接收的指令进行判断，并进行相应的响应
    (sensorData.led == 1) ? IoTGpioSetOutputVal(LED_TASK_GPIO, GPIO_HIGH) : IoTGpioSetOutputVal(LED_TASK_GPIO, GPIO_LOW);
    (sensorData.fan == 1) ? IoTGpioSetOutputVal(FAN_TASK_GPIO, GPIO_LOW) : IoTGpioSetOutputVal(FAN_TASK_GPIO, GPIO_HIGH);
    (sensorData.sig == 1) ? IoTGpioSetOutputVal(SIG_TASK_GPIO, GPIO_LOW) : IoTGpioSetOutputVal(SIG_TASK_GPIO, GPIO_HIGH);
    (sensorData.sg == 1) ? engine_turn_left_90() : regress_middle();
    mqtt_send_task_intime();
    return ret_code;
}

// 向云端发送返回值
// 以供华为iotda校验数据发送是否成功
void send_cloud_request_code(const char *request_id, int ret_code, int request_len)
{
    char *request_topic = (char *)malloc(strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) +
                                         strlen(DEVICE_ID) + request_len + 1);
    if (request_topic != NULL)
    {
        memset_s(request_topic,
                 strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + request_len + 1,
                 0,
                 strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + request_len + 1);
        if (sprintf_s(request_topic,
                      strlen(DEVICE_ID) + strlen(MALLOC_MQTT_TOPIC_PUB_COMMANDS_REQ) + request_len + 1,
                      MQTT_TOPIC_PUB_COMMANDS_REQ, DEVICE_ID, request_id) > 0)
        {
            if (ret_code == 0)
            {
                MQTTClient_pub(request_topic, "{\"result_code\":0}", strlen("{\"result_code\":0}"));
            }
            else if (ret_code == 1)
            {
                MQTTClient_pub(request_topic, "{\"result_code\":1}", strlen("{\"result_code\":1}"));
            }
        }
        free(request_topic);
        request_topic = NULL;
    }
}
/**
 * @brief MQTT接收数据的回调函数
 */
int8_t mqttClient_sub_callback(unsigned char *topic, unsigned char *payload)
{
    if ((topic == NULL) || (payload == NULL))
    {
        return -1;
    }
    else
    {
        printf("topic: %s\r\n", topic);
        printf("payload: %s\r\n", payload);

        // 提取出topic中的request_id
        char request_id[50] = {0};
        int ret_code = 1; // 1为失败
        if (0 == strcpy_s(request_id, sizeof(request_id),
                          topic + strlen(DEVICE_ID) + strlen("$oc/devices//sys/commands/request_id=")))
        {
            printf("request_id: %s\r\n", request_id);
            // 解析JSON数据
            ret_code = Parsing_json_data(payload);
            send_cloud_request_code(request_id, ret_code, sizeof(request_id));
        }
    }
    return 0;
}

/**
 * @brief MQTT  接收消息任务
 */
void mqtt_recv_task(void)
{
    while (1)
    {
        MQTTClient_sub();
        sleep(MQTT_RECV_TASK_TIME);
    }
}

static void network_wifi_mqtt_example(void)
{
    // 外设的初始化
    // Init初始化
    // GpioSetDir设置GPIO的输出模式
    IoTGpioInit(LED_TASK_GPIO);
    IoTGpioSetDir(LED_TASK_GPIO, IOT_GPIO_DIR_OUT);

    IoTGpioInit(FAN_TASK_GPIO);
    IoTGpioSetDir(FAN_TASK_GPIO, IOT_GPIO_DIR_OUT);

    IoTGpioInit(SIG_TASK_GPIO);
    IoTGpioSetDir(SIG_TASK_GPIO, IOT_GPIO_DIR_OUT);

    // 设置初始电平，LED,FAN,SIG均为关闭状态
    // 因默认LED为高电平触发，FAN和SIG为低电平触发，故LED设置初始值为LOW，FAN和SIG初始值为HIGH
    IoTGpioSetOutputVal(LED_TASK_GPIO, GPIO_LOW);
    IoTGpioSetOutputVal(FAN_TASK_GPIO, GPIO_HIGH);
    IoTGpioSetOutputVal(SIG_TASK_GPIO, GPIO_HIGH);

    // 设置伺服电机初始位置居中
    regress_middle();

    // 初始化和设置I2C总线，将GPIO13设置为SDA总线，GPIO14设置为SCL总线
    IoTGpioInit(IOT_GPIO_IDX_13);
    hi_io_set_func(IOT_GPIO_IDX_13, HI_IO_FUNC_GPIO_13_I2C0_SDA);
    IoTGpioInit(IOT_GPIO_IDX_14);
    hi_io_set_func(IOT_GPIO_IDX_14, HI_IO_FUNC_GPIO_14_I2C0_SCL);

    p_MQTTClient_sub_callback = &mqttClient_sub_callback;

    // 连接WiFi
    if (WiFi_connectHotspots(WIFI_NAME, WIFI_PASSWORD) != WIFI_SUCCESS)
    {
        printf("[error] connectWiFiHotspots\r\n");
    }
    sleep(TASK_INIT_TIME);

    // 连接MQTT服务器
    if (MQTTClient_connectServer(SERVER_IP_ADDR, SERVER_IP_PORT) != WIFI_SUCCESS)
    {
        printf("[error] mqttClient_connectServer\r\n");
    }
    sleep(TASK_INIT_TIME);

    // 初始化MQTT客户端
    if (MQTTClient_init(MQTT_CLIENT_ID, MQTT_USER_NAME, MQTT_PASS_WORD) != WIFI_SUCCESS)
    {
        printf("[error] mqttClient_init\r\n");
    }
    sleep(TASK_INIT_TIME);

    // 订阅主题
    if (MQTTClient_subscribe(MQTT_TOPIC_SUB_COMMANDS) != WIFI_SUCCESS)
    {
        printf("[error] mqttClient_subscribe\r\n");
    }
    sleep(TASK_INIT_TIME);

    //  创建线程
    osThreadAttr_t options;
    options.name = "mqtt_send_task";
    options.attr_bits = 0;
    options.cb_mem = NULL;
    options.cb_size = 0;
    options.stack_mem = NULL;
    options.stack_size = TASK_STACK_SIZE;
    options.priority = osPriorityNormal;

    mqtt_send_task_id = osThreadNew((osThreadFunc_t)mqtt_send_task, NULL, &options);
    if (mqtt_send_task_id != NULL)
    {
        printf("ID = %d, Create mqtt_send_task_id is OK!\r\n", mqtt_send_task_id);
    }

    options.name = "mqtt_recv_task";
    options.stack_size = TASK_STACK_SIZE;
    mqtt_recv_task_id = osThreadNew((osThreadFunc_t)mqtt_recv_task, NULL, &options);
    if (mqtt_recv_task_id != NULL)
    {
        printf("ID = %d, Create mqtt_recv_task_id is OK!\r\n", mqtt_recv_task_id);
    }
}
SYS_RUN(network_wifi_mqtt_example);
